﻿using Microsoft.Extensions.Configuration;
using System.Data;
using System.Data.Common;
using System.Text.Json;

namespace Apex.Commons.ConfigurationCenter
{
    public class DBConfigurationProvider : ConfigurationProvider, IDisposable
    {
        private DBConfigOptions options;

        private ReaderWriterLockSlim lockObj = new ReaderWriterLockSlim();

        private bool isDisposed;

        public DBConfigurationProvider(DBConfigOptions options)
        {
            DBConfigurationProvider dBConfigurationProvider = this;
            this.options = options;
            TimeSpan interval = TimeSpan.FromSeconds(3.0);
            if (options.ReloadInterval.HasValue)
            {
                interval = options.ReloadInterval.Value;
            }

            if (!options.ReloadOnChange)
            {
                return;
            }

            ThreadPool.QueueUserWorkItem(delegate
            {
                while (!dBConfigurationProvider.isDisposed)
                {
                    dBConfigurationProvider.Load();
                    Thread.Sleep(interval);
                }
            });
        }

        public void Dispose()
        {
            isDisposed = true;
        }

        public override IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string? parentPath) // string parentPath
        {
            lockObj.EnterReadLock();
            try
            {
                return base.GetChildKeys(earlierKeys, parentPath);
            }
            finally
            {
                lockObj.ExitReadLock();
            }
        }

        public override bool TryGet(string key, out string value)
        {
            lockObj.EnterReadLock();
            try
            {
                return base.TryGet(key, out value);
            }
            finally
            {
                lockObj.ExitReadLock();
            }
        }

        public override void Load()
        {
            base.Load();
            IDictionary<string, string> dictionary = null;
            try
            {
                lockObj.EnterWriteLock();
                dictionary = base.Data.Clone();
                string tableName = options.TableName;
                base.Data.Clear();
                using IDbConnection dbConnection = options.CreateDbConnection();
                dbConnection.Open();
                DoLoad(tableName, dbConnection);
            }
            catch (DbException)
            {
                base.Data = dictionary;
                throw;
            }
            finally
            {
                lockObj.ExitWriteLock();
            }

            if (DBConfigurationHelper.IsChanged(dictionary, base.Data))
            {
                OnReload();
            }
        }

        private void DoLoad(string tableName, IDbConnection conn)
        {
            using IDbCommand dbCommand = conn.CreateCommand();
            dbCommand.CommandText = "select Name,Value from " + tableName + " where Id in(select Max(Id) from " + tableName + " group by Name)";
            using IDataReader dataReader = dbCommand.ExecuteReader();
            while (dataReader.Read())
            {
                string @string = dataReader.GetString(0);
                string string2 = dataReader.GetString(1);
                if (string2 == null)
                {
                    base.Data[@string] = string2;
                    continue;
                }

                string2 = string2.Trim();
                if ((string2.StartsWith("[") && string2.EndsWith("]")) || (string2.StartsWith("{") && string2.EndsWith("}")))
                {
                    TryLoadAsJson(@string, string2);
                }
                else
                {
                    base.Data[@string] = string2;
                }
            }
        }

        private void LoadJsonElement(string name, JsonElement jsonRoot)
        {
            if (jsonRoot.ValueKind == JsonValueKind.Array)
            {
                int num = 0;
                foreach (JsonElement item in jsonRoot.EnumerateArray())
                {
                    string name2 = name + ConfigurationPath.KeyDelimiter + num;
                    LoadJsonElement(name2, item);
                    num++;
                }
            }
            else if (jsonRoot.ValueKind == JsonValueKind.Object)
            {
                foreach (JsonProperty item2 in jsonRoot.EnumerateObject())
                {
                    string name3 = name + ConfigurationPath.KeyDelimiter + item2.Name;
                    LoadJsonElement(name3, item2.Value);
                }
            }
            else
            {
                base.Data[name] = jsonRoot.GetValueForConfig();
            }
        }

        private void TryLoadAsJson(string name, string value)
        {
            JsonDocumentOptions jsonDocumentOptions = default(JsonDocumentOptions);
            jsonDocumentOptions.AllowTrailingCommas = true;
            jsonDocumentOptions.CommentHandling = JsonCommentHandling.Skip;
            JsonDocumentOptions jsonDocumentOptions2 = jsonDocumentOptions;
            try
            {
                JsonElement rootElement = JsonDocument.Parse(value, jsonDocumentOptions2).RootElement;
                LoadJsonElement(name, rootElement);
            }
            catch (JsonException)
            {
                base.Data[name] = value;
            }
        }
    }
}
